Listings 
Detlef Wilkening: Das Builder-Pattern und Alternativen in C++


Listing 1: Beispiel für einen schlecht lesbaren Code
class Widget
{
public:
    Widget(int x, int y, int w, int h, const string& title, 
                  bool enable, bool visible, bool readonly, bool checked, bool grayed, bool focused);
};

Widget w(2, 4, 60, 40, "Titel", true, false, true, false, false, true);

---------

Listing 2: Das besser lesbare Widget-Beispiel
template<int N> class Int // Abschnitt 1
{
    int value;
public:
    static constexpr int n = N;

    explicit Int(int value = 0) : value(value) {}

    Int<N> operator+(Int<N> rhs) const { return Int<N>(value + arg.value); }

    ostream& print(ostream& out) const
    {
      return out << value;
    }
};

template<int N> ostream& operator<<(ostream& out, Int<N> arg)
{
    return arg.print(out);
}

using xarg_t = Int<0>;
using yarg_t = Int<1>;
...
class Widget // Abschnitt 2
{
    xarg_t x_;
    yarg_t y_;
    enable_t enable_;
    visible_t visible_;
    checked_t checked_;

public:
    Widget(xarg_t x, yarg_t y, enable_t enable, visible_t visible, checked_t checked)
      : x_(x), y_(y), enable_(enable), visible_(visible), checked_(checked) {}

    friend ostream& operator<<(ostream& out, const Widget& w)
    {
      return out << "Widget: " << w.x_ << '/' << w.y_ << ' ' << w.enable_ << ' ' << w.visible_ << ' ' << w.checked_;
    }
};

---------

Listing 3: Alle Parameter gesammelt in der Datenklasse WidgetData
class WidgetData
{
public:
    WidgetData() {}

    WidgetData& x(int arg) { x_ = arg; return *this; }
    WidgetData& y(int arg) { y_ = arg; return *this; }
    WidgetData& title(const string& arg) { title_ = arg; return *this; }
    WidgetData& enable(bool arg) { enable_ = arg; return *this; }
    WidgetData& visible(bool arg) { visible_ = arg; return *this; }
    WidgetData& checked(bool arg) { checked_ = arg; return *this; }

    ostream& print(ostream& out) const
    {
      return out << "WidgetData " << x_ << '/' << y_ << " -> " << title_ << ' ' << enable_ << ' ' << visible_ << ' ' << checked_;
    }

private:
    int x_ = 0, y_ = 0;
    string title_ = "Widget"s;
    bool enable_ = true;
    bool visible_ = true;
    bool checked_ = false;
};

inline ostream& operator<<(ostream& out, const WidgetData& w)
{
    return w.print(out);
}


WidgetData w1;
cout << w1 << endl;

WidgetData w2;
w2.x(100).y(120).title("Button").visible(false);
cout << w2 << endl;

cout << WidgetData().enable(false).x(50).y(60).checked(true).title("Button") << endl;
...

---------

Listing 4: Einsatz des Builder-Patterns
class Widget
{
    WidgetData data_;

public:
    Widget(IStorage& in) : data_(in) {}
    void store(OStorage& out) const 
    {
      data_.store(out);
    }
    ...
};

---------

Listing 5: Builder-Pattern mit dedizierten Werttypen
class WidgetData
{
    ...
    WidgetData& enable(enable_t arg) { enable_ = arg; return *this; }
    WidgetData& visible(visible_t arg) { visible_ = arg; return *this; }
    WidgetData& checked(checked_t arg) { checked_ = arg; return *this; }
    ...
};

WidgetData().enable(disable).checked(gray_checked);

---------

Listing 6: Statusübergabe mit Setter-Funktionen
class WidgetData
{
    ...
    WidgetData& set(enable_t arg) { enable_ = arg; return *this; }
    WidgetData& set(visible_t arg) { visible_ = arg; return *this; }
    WidgetData& set(checked_t arg) { checked_ = arg; return *this; }
    ...
};

WidgetData().set(disable).set(gray_checked);

---------

Listing 7: Variadic-Template mit Parameterabsplittung
template<class T> void print(T t)
{
    cout << t << endl;
}

template<class T, class... Args> void print(T t, Args... args)
{
    cout << t << ' ';
    print(args...);
}

print(true, 2.71, "C++");
print(1, 2, 3.14, "Variadic", "Templates"s, false);

---------

Listing 8: Die WidgetData-Klasse mit drei einfachen Typen
...
class WidgetData
{
    int x_ = 0;
    string title_ = "Widget"s;
    bool checked_ = false;

public:
    template<class... Ts> WidgetData(Ts&& ... ts)
    {
      init(forward<Ts>(ts)...);
    }

    void init() {}
    template<class T, class... Ts> void init(T&& t, Ts&& ... ts)
    {
      set(t);
      init(forward<Ts>(ts)...);
    }

    void set(int x) { x_ = x; }
    void set(const string& t) { title_ = t; }
    void set(bool c) { checked_ = c; }
};

---------

Listing 9: Die entstandene Widget-Klasse mit Nutzungsmöglichkeit
class WidgetData
{
public:
    template<class... Ts> WidgetData(Ts&& ... ts)
    {
      init(forward<Ts>(ts)...);
    }

    void init() {}
    template<class T, class... Ts> void init(T&& t, Ts&& ...ts)
    {
      set(t);
      init(forward<Ts>(ts)...);
    }

    void set(xarg_t x) { x_ = x; }
    void set(yarg_t y) { y_ = y; }
    void set(const string& t) { title_ = t; }
    void set(enable_t arg) { enable_ = arg; }
    void set(visible_t arg) { visible_ = arg; }
    void set(checked_t arg) { checked_ = arg; }
    ...

WidgetData w1;
cout << w1 << endl;

WidgetData w2(xarg_t(100), yarg_t(120), "Button"s, checked);
cout << w2 << endl;

cout << WidgetData(gray_checked, yarg_t(50), disable, xarg_t(60), "Button"s) << endl;

---------

Listing 10: Das Beispiel mit User-defined Literals
template<int N> class Int // Int-Klasse verändern
{
    ...
    explicit constexpr Int(int value = 0) : value(value) {}
    ...
constexpr xarg_t operator "" _x(unsigned long long v) // User-defined Literals
{
    return xarg_t(static_cast<int>(v));
}

constexpr yarg_t operator "" _y(unsigned long long v)
{
    return yarg_t(static_cast<int>(v));
}
...
WidgetData w1; // Die neuen Aufrufe
cout << w1 << endl;

WidgetData w2(100_x, 120_y, "Button"s, checked);
cout << w2 << endl;

cout << WidgetData(gray_checked, 50_y, disable, 60_x, "Button"s) << endl;

---------

Listing 11: Benannte Parameter in C++ simulieren
...
template<typename T> struct Parameter
{
    template<class T2> T operator=(T2 arg) { return T(arg); }
};

Parameter<xarg_t> x_arg;
Parameter<yarg_t> y_arg;
Parameter<string> title_arg;
Parameter<enable_t> enable_arg;
Parameter<visible_t> visible_arg;
Parameter<checked_t> checked_arg;

class WidgetData
{
public:
    template<class... Ts> WidgetData(Ts&& ... ts)
    {
      init(forward<Ts>(ts)...);
    }

    void init() {}
    template<class T, class... Ts> void init(T&& t, Ts&& ...ts)
    {
      set(t);
      init(forward<Ts>(ts)...);
    }

    void set(xarg_t x) { x_ = x; }
    void set(yarg_t y) { y_ = y; }
    void set(const string& t) { title_ = t; }
    void set(enable_t arg) { enable_ = arg; }
    void set(visible_t arg) { visible_ = arg; }
    void set(checked_t arg) { checked_ = arg; }
    ...
};
.. .
WidgetData w2(x_arg = 100, y_arg = 120, title_arg = "Button"s, checked_arg = checked);
cout << w2 << endl;

cout << WidgetData(checked_arg = gray_checked, y_arg = 50, enable_arg = disable, x_arg = 60, title_arg = "Button"s) << endl;

